// 
//  Copyright © 2009 Jiří Zárevúcky <zarevucky.jiri@gmail.com>
// 
//  This program is free software: you can redistribute it and/or modify
//  it under the terms of the GNU Affero General Public License as
//  published by the Free Software Foundation, either version 3 of the
//  License, or (at your option) any later version.
// 
//  This program is distributed in the hope that it will be useful,
//  but WITHOUT ANY WARRANTY; without even the implied warranty of
//  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
//  GNU Affero General Public License for more details.
// 
//  You should have received a copy of the GNU Affero General Public License
//  along with this program.  If not, see <http://www.gnu.org/licenses/>.
// 
// 


using System;
using System.Collections.Generic;

using Galaxium.Protocol.Xmpp.Library.Core;
using Galaxium.Protocol.Xmpp.Library.Xml;

namespace Galaxium.Protocol.Xmpp.Library.Extensions
{
	public class DataFormEventArgs: EventArgs
	{
		public DataForm Form { get; private set; }
		public bool IsLegacy { get; private set; }
		
		public DataFormEventArgs (DataForm form, bool is_legacy)
		{
			Form = form;
			IsLegacy = is_legacy;
		}
	}
	
	public class Registration
	{
		public event EventHandler<StanzaErrorEventArgs> Failed;
		public event EventHandler CancelSucceeded;
		public event EventHandler<DataFormEventArgs> FormReceived;
		
		private CoreStream _stream;
		private JabberID _jid;
		
		public bool IsRegistered { get; private set; }
	
		public Registration (CoreStream stream, JabberID jid)
		{
			_stream = stream;
			_jid = jid;
		}
		
		public void RetrieveForm ()
		{
			var request = new Iq (IqType.Get, Namespaces.Registration);
			request.To = _jid;
			_stream.SendQuery (request, HandleForm);
		}
		
		public void Cancel ()
		{
			var iq = new Iq (IqType.Set, Namespaces.Registration);
			iq.To = _jid;
			iq.Query.AppendChild (new Element ("remove"));
			_stream.SendQuery (iq, FinishCancel);
		}
				
		private void HandleForm (Iq result)
		{
			if (result.IsError) {
				OnFailed (result == null ? null : result.Error);
				return;
			}
			
			IsRegistered = result.Query.FirstChild ("registered", null) != null;
			
			var form = result.Query.FirstChild ("x", Namespaces.DataForms);
			
			if (form == null)
				OnFormReceived (TransformLegacyForm (result.Query), true);
			else
				OnFormReceived (new DataForm (form, SubmitForm), false);
		}
		
		private DataForm TransformLegacyForm (Element query)
		{
			var form = new DataForm (FormAction.Form, SubmitLegacy);
			form.Title = "Registration with " + _jid;
			form.Instructions = query.GetTextChild ("instructions", null);
			
			RetrieveField (form, query, "username", "User name" , DataFieldType.TextSingle);
			RetrieveField (form, query, "nick"    , "Nickname"  , DataFieldType.TextSingle);
			RetrieveField (form, query, "password", "Password"  , DataFieldType.TextPrivate);
			RetrieveField (form, query, "name"    , "Name"      , DataFieldType.TextSingle);
			RetrieveField (form, query, "first"   , "First name", DataFieldType.TextSingle);
			RetrieveField (form, query, "last"    , "Last name" , DataFieldType.TextSingle);
			RetrieveField (form, query, "email"   , "E-Mail"    , DataFieldType.TextSingle);
			RetrieveField (form, query, "address" , "Address"   , DataFieldType.TextSingle);
			RetrieveField (form, query, "city"    , "City"      , DataFieldType.TextSingle);
			RetrieveField (form, query, "state"   , "State"     , DataFieldType.TextSingle);
			RetrieveField (form, query, "zip"     , "ZIP code"  , DataFieldType.TextSingle);
			RetrieveField (form, query, "phone"   , "Phone"     , DataFieldType.TextSingle);
			RetrieveField (form, query, "url"     , "URL"       , DataFieldType.TextSingle);
			RetrieveField (form, query, "date"    , "Date"      , DataFieldType.TextSingle);
			RetrieveField (form, query, "misc"    , null        , DataFieldType.Hidden);
			RetrieveField (form, query, "text"    , null        , DataFieldType.Hidden);
			RetrieveField (form, query, "key"     , null        , DataFieldType.Hidden);
			
			return form;
		}
		
		private void RetrieveField (DataForm form, Element query,
		                            string name, string label, DataFieldType type)
		{
			var elm = query.FirstChild (name, null);
			if (elm == null) return;
			var field = new DataField (name, type);
			field.Label = label;
			field.SetString (elm.Text);
			field.IsRequired = true;
			form.AddField (field);
		}
		
		private void FinishCancel (Iq result)
		{
			if (result == null) {
				OnFailed (null);
			}
			if (result.IsError) {
				var form = result.Query.FirstChild ("x", Namespaces.DataForms);
				if (form == null) OnFailed (result.Error);
				else OnFormReceived (new DataForm (form, SubmitForm), false);
			}
			else OnCancelSucceeded ();
		}
		
		#region Private submit handlers
		
		private void Submit (DataFormSubmitArgs args, Action<Element> query_handler)
		{
			var iq = new Iq (IqType.Set);
			iq.To = _jid;
			var q = new Element ("query", Namespaces.Registration);
			iq.AppendChild (q);
			query_handler (q);
			try { _stream.SendQuery (iq, -1); }
			catch (QueryException e) { args.Error = e.Stanza.Error; }

		}
		
		private void SubmitForm (DataFormSubmitArgs args)
		{
			Submit (args, (q) => q.AppendChild (args.Form.ToXml (true)));
		}
		
		private void SubmitLegacy (DataFormSubmitArgs args)
		{
			Submit (args, (q) => {
				foreach (DataField field in args.Form)      // set legacy elements
					q.AppendTextChild (field.Identifier, null, field.GetString () ?? String.Empty);
			});
		}
		
		#endregion
		
		#region Event invokers
		
		private void OnCancelSucceeded ()
		{
			if (CancelSucceeded != null)
				CancelSucceeded (this, EventArgs.Empty);
		}
		
		private void OnFailed (StanzaError error)
		{
			if (Failed != null)
				Failed (this, new StanzaErrorEventArgs (error));
		}
		
		private void OnFormReceived (DataForm form, bool is_legacy)
		{
			if (FormReceived != null)
				FormReceived (this, new DataFormEventArgs (form, is_legacy));
		}
		
		#endregion
	}
}
